from math import *  # type: ignore
from typing import List, Tuple, Union, Iterable
import numpy as np


r_p = lambda r0, r1: r0 * r1 / (r0 + r1)

v_rs = lambda vin, r0, r1: vin / (r0 + r1) * r1

v_431 = lambda r1, r2: 2.5 * (1 + r1 / r2)

ik_431 = lambda vin, vout, r0: (vin - vout) / r0

v_mid_ref = lambda v_ref, r1, r2: v_ref * (1 + r1 / r2)

r_ratio_431_max = lambda vin, vout: (vin / vout) - 1


def regulator_feedback_ratio(vout, vref=0.6):
    """
    根据输出电压，计算反馈电阻的比值
    """
    return vout / vref - 1


def regulator_vout(fb_ratio, vref=0.6):
    """
    根据输出电压，计算反馈电阻的比值
    """
    return vref * (1 + fb_ratio)


def db(out_val, in_val=1):
    return 20 * log10(out_val / in_val)


bd = lambda db_val: 10 ** (db_val / 20)

kcal2kj = lambda kcal: kcal * 4.184

kj2kcal = lambda kj: kj / 4.184


# 把常用的矩阵运算函数插入到矩阵类中
m_norm = lambda x: np.linalg.norm(x)

np.matrix.norm = m_norm

m_det = lambda x: np.linalg.det(x)

np.matrix.det = m_det


# 基于输入的一维数组创建一个方阵，若不指定行 / 列数m，则自动计算
def sqmat(l: Iterable, m: int = 0):
    l_len = len(l)
    if m == 0:
        m = int(sqrt(l_len))

    if m * m != l_len:
        raise ValueError("元素数量和行列数不匹配")

    return np.matrix(l).reshape(m, m)


# 基于输入的一维数组创建一个任意矩阵，若不指定列数n，则自动计算
def anymat(l: Iterable, m: int, n: int = 0):
    l_len = len(l)
    if n == 0:
        n = int(l_len / m)

    if m * n != l_len:
        raise ValueError("元素数量和行列数不匹配")

    return np.matrix(l).reshape(m, n)


_E24_VALUE_TABLE = (
    1.0,
    1.1,
    1.2,
    1.3,
    1.5,
    1.6,
    1.8,
    2.0,
    2.2,
    2.4,
    2.7,
    3.0,
    3.3,
    3.6,
    3.9,
    4.3,
    4.7,
    5.1,
    5.6,
    6.2,
    6.8,
    7.5,
    8.2,
    9.1,
)


def find_e24_resistor_pair(target_ratio: float, result_len=10) -> List[Tuple[float, float, float]]:
    """
    在 E24 优先数系中找到两个电阻值，使它们的比值最接近给定的比值。
    如果有多个解，返回所有较小值不相等且在 1~10 范围内的解。
    如果没有精确匹配的解，返回偏差最小的一组解。
    每个解都是一个三元组 (r1, r2, diff)，其中 r1 和 r2 是电阻值，diff 是偏差的绝对值。

    :param target_ratio: 目标比值
    :param result_len: 所需结果的数量，如果精确匹配的数量不够，就用偏差较小的结果补上
    :return: 电阻值组合列表
    """
    e24 = [
        1.0,
        1.1,
        1.2,
        1.3,
        1.5,
        1.6,
        1.8,
        2.0,
        2.2,
        2.4,
        2.7,
        3.0,
        3.3,
        3.6,
        3.9,
        4.3,
        4.7,
        5.1,
        5.6,
        6.2,
        6.8,
        7.5,
        8.2,
        9.1,
    ]
    result = []
    min_result = []
    epsilon = 1e-9
    min_exponent = floor(log10(e24[0] * target_ratio))
    max_exponent = min_exponent + 1
    for r2 in e24:
        for r1_base in e24:
            for i in range(min_exponent, max_exponent + 1):
                r1 = r1_base * (10**i)
                ratio = r1 / r2
                diff = abs(ratio - target_ratio)
                if diff < epsilon:
                    result.append((r1, r2, diff))
                else:
                    min_result.append((r1, r2, diff))
                    min_result.sort(key=lambda x: x[2])
                    if len(min_result) > result_len:
                        min_result.pop()

    if len(result) < result_len:
        for i in range(result_len - len(result)):
            if i == len(min_result):
                break
            result.append(min_result[i])

    return result


def print_resistor_pair(target_ratio: float, result_len=10):
    resistors = find_e24_resistor_pair(target_ratio, result_len)

    print("比值匹配的组合为：")

    i = 0
    while i < len(resistors):
        r = resistors[i]
        if r[2] > 1e-9:
            break
        print(f"{r[0]:.1f}, \t{r[1]:.1f}")
        i += 1

    print("误差较小的组合为：")

    while i < len(resistors):
        r = resistors[i]
        diff_ratio = r[2] / target_ratio * 100
        print(f"{r[0]:.1f}, \t{r[1]:.1f}, \t误差: {diff_ratio:.4f}%")
        i += 1


def resistor_ratio_range(r1: float, r2: float, tol1: float = 1, tol2: float = 1) -> (float, float):
    """
    计算两个电阻比例的实际范围。

    :param r1: 第一个电阻的标称值，单位为欧姆。
    :param r2: 第二个电阻的标称值，单位为欧姆。
    :param tol1: 第一个电阻的误差等级，单位为百分比。
    :param tol2: 第二个电阻的误差等级，单位为百分比。
    :return: 一个元组，包含实际比例的最小值和最大值。
    """
    r1_min = r1 * (1 - tol1 / 100)
    r1_max = r1 * (1 + tol1 / 100)
    r2_min = r2 * (1 - tol2 / 100)
    r2_max = r2 * (1 + tol2 / 100)
    ratio_min = r1_min / r2_max
    ratio_max = r1_max / r2_min
    print(f"实际比例范围: {ratio_min:.3f} 到 {ratio_max:.3f}")
    ratio = r1 / r2
    min_err_percent = (ratio_min - ratio) / ratio * 100
    max_err_percent = (ratio_max - ratio) / ratio * 100
    print(f"误差范围是: {min_err_percent:.4f}% ~ {max_err_percent:.4f}%")
    return (ratio_min, ratio_max)


def sina(angle) -> float:
    return sin(radians(angle))


def cosa(angle) -> float:
    return cos(radians(angle))


def tana(angle) -> float:
    return tan(radians(angle))


def gain_drift_by_gain_error(ge_h: float, ge_l: float, temp_range: float = 180) -> float:
    """
    根据增益误差计算增益温漂.

    :param ge_h: 高温GE %
    :param ge_l: 低温GE %
    :param norminal_gain: 标称增益
    :param temp_range: 温度范围
    :return: ppm/℃
    """
    d_ge = abs(ge_h - ge_l)
    return d_ge / temp_range * 1e4


def gain_drift(gh: float, gl: float, norminal_gain: float, temp_range: float = 180) -> float:
    """
    根据增益计算增益温漂.

    :param gh: 高温增益
    :param gl: 低温增益
    :param norminal_gain: 标称增益
    :param temp_range: 温度范围
    :return: ppm/℃
    """
    dg = abs(gh - gl)
    return dg / norminal_gain / temp_range * 1e6


def vos_drift(vos_h: float, vos_l: float, temp_range: float = 180) -> float:
    """
    计算失调电压温漂
    :vos_h: 高温失调电压 μV
    :vos_l: 低温失调电压 μV
    :return: μV/℃
    """
    return abs((vos_h - vos_l) / temp_range)


def v_drift_ppm(vh: float, vl: float, norminal_v: float, temp_range: float = 180) -> float:
    """
    计算输出电压温漂
    :param vh: 高温电压 V
    :param vos_l: 低温电压 V
    :param norminal_v: 标称电压 V
    :return: ppm/℃
    """
    dv = abs(vh - vl)
    return dv / norminal_v / temp_range * 1e6
